package kernel_gui.gui.extended;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.util.List;

import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.table.DefaultTableModel;

import kernel_gui.gui.generic.MillionaireFrame;
import kernel_gui.gui.generic.graphic.ConnectionsFrame;

import common.entities.enums.ACTION;
import common.entities.enums.ENTITIES;
import common.entities.persistent.Actor;
import common.entities.persistent.Country;
import common.entities.persistent.Director;
import common.entities.persistent.Genre;
import common.entities.persistent.Movie;

@SuppressWarnings("serial")
public class EConnectionsFrame extends ConnectionsFrame {

	public enum CONNECTION_TYPE {Actor, Country, Genre, Director, Movie};
	
	private CONNECTION_TYPE ctMaster;
	private CONNECTION_TYPE ctSlave;
	
	/*Only possible masters*/
	private Movie movie = null;
	private Actor actor = null;
	private Director director = null;
	
	
	/*Data in shiat*/
	private DefaultTableModel currentTableModel;
	private List<Movie> movies;
	private List<Actor> actors;
	private List<Director> directors;
	private List<Country> countries;
	private List<Genre> genres;
	
	private int count = 0;
	private int currentPage = 0;
	private int currentPageCount = 0;
	private int totalPages = 0;
	private int numberOfRecordsPerPage = 0;
	
	public EConnectionsFrame(CONNECTION_TYPE ctMaster, Object master, CONNECTION_TYPE ctSlave){
		/*Command to execute on cancel*/
		setExecuteOnCancel(new CommandToExecute() {
			
			@Override
			public void command(Object answer) {
				Component components[] = MainPanel.getComponents();
				for (Component c : components) {
					if(c instanceof JPanel){
						JPanel panel = (JPanel) c;
						Component pcomponents[] = panel.getComponents();
						for(Component pc : pcomponents){
							if(pc instanceof JPanel){
								JPanel anotherPanel = (JPanel) pc;
								Component pccomponents[] = anotherPanel.getComponents();
								for(Component pcc : pccomponents){
									pcc.setEnabled(true);
								}
							}
							pc.setEnabled(true);
						}
					}
					
					c.setEnabled(true);
				}
				ResultTable.setEnabled(true);
				updateButtonsAndLabelsAfterReturnedResultSet();
			}
		});
		
		/*Command to execute before long request*/
		setExecuteBeforeLongRequest(new CommandToExecute() {
			
			@Override
			public void command(Object answer) {
				Component components[] = MainPanel.getComponents();
				for (Component c : components) {
					if(c instanceof JPanel){
						JPanel panel = (JPanel) c;
						Component pcomponents[] = panel.getComponents();
						for(Component pc : pcomponents){
							if(pc instanceof JPanel){
								JPanel anotherPanel = (JPanel) pc;
								Component pccomponents[] = anotherPanel.getComponents();
								for(Component pcc : pccomponents){
									pcc.setEnabled(false);
								}
							}
							pc.setEnabled(false);
						}
					}
					
					c.setEnabled(false);
				}
				ResultTable.setEnabled(false);
			}
		});
		
		
		this.ctMaster = ctMaster;
		this.ctSlave = ctSlave;
		
		switch(ctMaster){
		case Actor:
			this.actor = (Actor)master;
			this.setTitle(actor.getLastName() +", " + actor.getFirstName() + " - Movies Connections");
			break;
		case Movie:
			this.movie = (Movie)master;
			this.setTitle(movie.getTitle() +"  (" + movie.getReleaseYear() + ") - " + ctSlave.toString() + " Connections");
			break;
		case Director:
			this.director = (Director)master;
			this.setTitle(director.getLastName() +", " + director.getFirstName() + " - Movies Connections");
			break;			
		}
		
		/*Update the numberOfRecords variable*/
		NumOfRecordsInPageComboBox.setSelectedIndex(2);
		numberOfRecordsPerPage = Integer.parseInt(NumOfRecordsInPageComboBox.getSelectedItem().toString());
		
		countResultAndPopulateTable(1);
	}
	
	private void initTableModel(){
		switch(this.ctSlave){
		case Actor:
			currentTableModel = new DefaultTableModel(new Object[][]{}, new Object[]{"First Name", "Last Name", "Sex"});
			break;
		case Movie:
			currentTableModel = new DefaultTableModel(new Object[][]{}, new Object[]{"Title", "Release Year", "Rank"});
			break;
		case Director:
			currentTableModel = new DefaultTableModel(new Object[][]{}, new Object[]{"First Name", "Last Name"});
			break;
		case Country:
			currentTableModel = new DefaultTableModel(new Object[][]{}, new Object[]{"Country Name"});
			break;
		case Genre:
			currentTableModel = new DefaultTableModel(new Object[][]{}, new Object[]{"Genre Name"});
			break;
		}
		
		ResultTable.setModel(currentTableModel);
	}
	
 	protected void countResultAndPopulateTable(int page){
		
		final int pageWhenFinished = page;
		/*Command to execute after returning from*/
		int requestID = executeBeforeLongRequest(new CommandToExecute() {
			
			@Override
			public void command(Object answer) {
				/*Updated count & labels*/
				EConnectionsFrame.this.count = Integer.parseInt(answer.toString());
				EConnectionsFrame.this.FoundNumberLabel.setText(numbers_nf.format(EConnectionsFrame.this.count));
				
				if(EConnectionsFrame.this.count != 0)
					EConnectionsFrame.this.currentPage = 1;
				else
					EConnectionsFrame.this.currentPage = 0;
				
				EConnectionsFrame.this.PageNumberLabel.setText(numbers_nf.format(EConnectionsFrame.this.currentPage));
				EConnectionsFrame.this.totalPages = (int) Math.ceil(((float)EConnectionsFrame.this.count)/numberOfRecordsPerPage);
				EConnectionsFrame.this.TotalPageNumberLabel.setText(numbers_nf.format(EConnectionsFrame.this.totalPages));
									
				/*Get the data*/
				populateTableWithResults(pageWhenFinished);
				
			}
		});
	
		/*Count the number of records according to the slave*/
		switch(ctSlave){
			case Actor:
				kernel.countActors(null, null, null, movie, requestID);
				break;
			case Director:
				kernel.countDirectors(null, null, movie, requestID);
				break;
			case Country:
				kernel.countCountries(null, movie, requestID);
				break;
			case Genre:
				kernel.countGenres(null, movie, requestID);
				break;
			case Movie:
				kernel.countMovies(null, -1, -1, -1, -1, actor, director, null, null, requestID);
				break;
		}
	}
	
	private void populateTableWithResults(int page){
		/*Command to execute after returning from*/
		
		final int pageNumberToSet = page;
		int requestID = executeBeforeLongRequest(new CommandToExecute() {
			
			@SuppressWarnings("unchecked")
			@Override
			public void command(Object answer) {
				if(answer != null){
					
					switch(ctSlave){
						case Actor:
							EConnectionsFrame.this.actors = (List<Actor>) answer;
							currentPageCount = EConnectionsFrame.this.actors.size();
							break;
						case Director:
							EConnectionsFrame.this.directors = (List<Director>) answer;
							currentPageCount = EConnectionsFrame.this.directors.size();
							break;
						case Country:
							EConnectionsFrame.this.countries = (List<Country>) answer;
							currentPageCount = EConnectionsFrame.this.countries.size();
							break;
						case Genre:
							EConnectionsFrame.this.genres = (List<Genre>) answer;
							currentPageCount = EConnectionsFrame.this.genres.size();
							break;
						case Movie:
							EConnectionsFrame.this.movies = (List<Movie>) answer;
							currentPageCount = EConnectionsFrame.this.movies.size();
						break;
					}
					
					/*Create new table model, and insert the rows*/
					initTableModel();
					
					Object[] rowData = null;
					for(int i = 0; i < currentPageCount; i++){
						/*Get data according to current slave*/
						switch(ctSlave){
							case Actor:{
								Actor actor = EConnectionsFrame.this.actors.get(i);
								rowData = new Object[] {actor.getFirstName(), actor.getLastName(), actor.getSex().toString()};
								break;
							}case Director:{
								Director director = EConnectionsFrame.this.directors.get(i);
								rowData = new Object[] {director.getFirstName(), director.getLastName()};
								break;
							}case Country:{
								Country country = EConnectionsFrame.this.countries.get(i);
								rowData = new Object[] {country.getName()};
								break;
							}case Genre:{
								Genre genre = EConnectionsFrame.this.genres.get(i);
								rowData = new Object[] {genre.getName()};
								break;
							}case Movie:{
								Movie movie = EConnectionsFrame.this.movies.get(i);
								rowData = new Object[] {movie.getTitle(), movie.getReleaseYear(), movie.getRank()};
							break;
							}
						}
						currentTableModel.insertRow(i, rowData);
					}
					
					
					if(EConnectionsFrame.this.count != 0)
						currentPage = pageNumberToSet;
					else
						currentPage = 0;
					
					updateButtonsAndLabelsAfterReturnedResultSet();
				}
			}
		});
		
		int from = (page <= 0) ? 0 : (page-1)*numberOfRecordsPerPage;
		int to = (page <= 0) ? 0 : page*numberOfRecordsPerPage;
		
		switch(ctSlave){
			case Actor:
				kernel.getMovieEntities(movie, ENTITIES.ACTOR, from, to, requestID);
				break;
			case Director:
				kernel.getMovieEntities(movie, ENTITIES.DIRECTOR, from, to, requestID);
				break;
			case Country:
				kernel.getMovieEntities(movie, ENTITIES.COUNTRY, from, to, requestID);
				break;
			case Genre:
				kernel.getMovieEntities(movie, ENTITIES.GENRE, from, to, requestID);
				break;
			case Movie:
				if(ctMaster == CONNECTION_TYPE.Actor)
					kernel.getActorMovies(actor, from, to, requestID);
				else/*has to be a director*/
					kernel.getDirectorMovies(director, from, to, requestID);
				break;
		}
	}
	
	private void updateButtonsAndLabelsAfterReturnedResultSet(){
		PageNumberLabel.setText(String.valueOf(currentPage));
		totalPages = (int) Math.ceil(((float)count)/numberOfRecordsPerPage);
		TotalPageNumberLabel.setText(String.valueOf(totalPages));
		
		if(totalPages == 0){
			NextButton.setEnabled(false);
			PreviousButton.setEnabled(false);
			NumOfRecordsInPageComboBox.setEnabled(false);
		}else{
			NextButton.setEnabled(true);
			PreviousButton.setEnabled(true);
			NumOfRecordsInPageComboBox.setEnabled(true);
		}
					
	}
	
	
	@Override
	protected void NumOfRecordsInPageComboBoxActionPerformed(ActionEvent evt) {
		numberOfRecordsPerPage = Integer.parseInt(NumOfRecordsInPageComboBox.getSelectedItem().toString());
		
		/*fix problem when the user is on page 100 in 5rec-per-page but there is no page 100 in 100rec-per-page*/
		int expectedTotalPages = (int) Math.ceil(((float)EConnectionsFrame.this.count)/numberOfRecordsPerPage);

		
		if(currentPage > expectedTotalPages)
			currentPage = expectedTotalPages;
		
		populateTableWithResults(currentPage);		
	}

	@Override
	protected void NextButtonActionPerformed(ActionEvent evt) {
		if(currentPage < totalPages){
			int askingPage = currentPage+1;
			populateTableWithResults(askingPage);
		}else{
			populateTableWithResults(1);
		}		
	}

	@Override
	protected void PreviousButtonActionPerformed(ActionEvent evt) {
		if(currentPage > 1){
			int askingPage = currentPage-1;
			populateTableWithResults(askingPage);
		}else{
			populateTableWithResults(totalPages);
		}		
	}

	@Override
	protected void DeleteButtonActionPerformed(ActionEvent evt) {
		int[] selectedRowIndexes = ResultTable.getSelectedRows();
		if(selectedRowIndexes.length != 1){
			JOptionPane.showMessageDialog(this, "Please select ONE record to delete");
		}else{
			/*Command to execute after returning from*/
			int requestID = executeBeforeLongRequest(new CommandToExecute() {
				
				@Override
				public void command(Object answer) {
					//TODO - check if this is always right
					if(currentPageCount != 1)
						EConnectionsFrame.this.countResultAndPopulateTable(currentPage);
					else
						EConnectionsFrame.this.countResultAndPopulateTable(--currentPage);
				}
			});
			
				
			
			switch(ctSlave){
			case Actor:
				kernel.actionEntityFromMovie(ACTION.DELETE, movie, ENTITIES.ACTOR, 
						this.actors.get(ResultTable.convertRowIndexToModel(selectedRowIndexes[0])).getId(), requestID);
				break;
			case Director:
				kernel.actionEntityFromMovie(ACTION.DELETE, movie, ENTITIES.DIRECTOR, 
						this.directors.get(ResultTable.convertRowIndexToModel(selectedRowIndexes[0])).getId(), requestID);
				break;
			case Country:
				kernel.actionEntityFromMovie(ACTION.DELETE, movie, ENTITIES.COUNTRY, 
						this.countries.get(ResultTable.convertRowIndexToModel(selectedRowIndexes[0])).getId(), requestID);
				break;
			case Genre:
				kernel.actionEntityFromMovie(ACTION.DELETE, movie, ENTITIES.GENRE, 
						this.genres.get(ResultTable.convertRowIndexToModel(selectedRowIndexes[0])).getId(), requestID);
				break;
			case Movie:
				if(ctMaster == CONNECTION_TYPE.Actor)
					kernel.deleteMovieFromActor(actor, this.movies.get(ResultTable.convertRowIndexToModel(selectedRowIndexes[0])), requestID);
				else/*has to be a director*/
					kernel.deleteMovieFromDirector(director, this.movies.get(ResultTable.convertRowIndexToModel(selectedRowIndexes[0])), requestID);
				break;
			}
		}
	}

	@Override
	protected void InsertButtonActionPerformed(ActionEvent evt) {
		Class<? extends MillionaireFrame> frameToOpenClass = null;
		
		switch(ctSlave){
		case Actor:
			frameToOpenClass = EControlActors.class;
			break;
		case Director:
			frameToOpenClass = EControlDirectors.class;
			break;
		case Country:
			frameToOpenClass = EControlCountries.class;
			break;
		case Genre:
			frameToOpenClass = EControlGenres.class;
			break;
		case Movie:
			frameToOpenClass = EControlMovies.class;
			break;
		}
		
		openAnotherFrame(frameToOpenClass, false, new CommandToExecute() {
			
			@Override
			public void command(Object answer) {
				if(answer != null){
					/*Populate after insert finishes*/
					int requestID = executeBeforeLongRequest(new CommandToExecute() {
						
						@Override
						public void command(Object answer) {
							//TODO - check if this is always right
							EConnectionsFrame.this.countResultAndPopulateTable((currentPage == 0) ? 1 : currentPage);
						}
					});
					
					switch(ctSlave){
						case Actor:
							kernel.actionEntityFromMovie(ACTION.CREATE, movie, ENTITIES.ACTOR,
									((Actor)answer).getId(), requestID);
							break;
						case Director:
							kernel.actionEntityFromMovie(ACTION.CREATE, movie, ENTITIES.DIRECTOR,
									((Director)answer).getId(), requestID);
							break;
						case Country:
							kernel.actionEntityFromMovie(ACTION.CREATE, movie, ENTITIES.COUNTRY,
									((Country)answer).getId(), requestID);
							break;
						case Genre:
							kernel.actionEntityFromMovie(ACTION.CREATE, movie, ENTITIES.GENRE,
									((Genre)answer).getId(), requestID);
							break;
						case Movie:
							//TODO - there were no insert-movie-connection for Director/Actor - so this is
							//the solution (simpler..) is it ok?
							if(ctMaster == CONNECTION_TYPE.Actor)
								kernel.actionEntityFromMovie(ACTION.CREATE, ((Movie)answer), ENTITIES.ACTOR,
										actor.getId(), requestID);
							else/*has to be a director*/
								kernel.actionEntityFromMovie(ACTION.CREATE, ((Movie)answer), ENTITIES.DIRECTOR,
										director.getId(), requestID);
							break;
					}
				}
			}
		});
		
		
	}

	@Override
	protected void BackButtonActionPerformed(ActionEvent evt) {
		returnToCallingFrame();
	}

	@Override
	protected void setWindowComponentsWhenCalledFromAnotherFrame() {
		/*not needed*/
		
	}

}
